/**
*
*	clipboard.c - Modulo generico gestionado por el clipboard manager
*
*/

#include "clipboard.h"

/* Licencia del modulo */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alvaro Perez Liaño - Xavier Gallofre Nieva");

/* Estructura de los ficheros en /proc */
static struct proc_dir_entry *proc_clipboard;
static struct proc_dir_entry *proc_period;

/* Estructura de los directorios en /proc */
static struct proc_dir_entry *proc_dir;
/* IMPORTANTE - Esta entrada viene dada por el clipboard manager */
extern struct proc_dir_entry *proc_dir_aiso;

/* task_struct para el kernel thread */
static struct task_struct *ts;

/* task_struct para el kernel thread que crea "PERIOD" */
static struct task_struct *ts_wake;
/* Entero que indica si el thread auxiliar esta funcionando o no */
/* 0 = sin arrancar | 1 = arrancado */
static short active_wake;

/* Buffers para la entradas clipboard y periodo */
static char *clipboard_buffer;
static char *period_buffer;

/* Buffer auxiliar para poder dar formato a la entrada del usuario*/
static char *my_buffer;

/**************************** INICIO FUNCIONES THREADS *****************************************/ 
/* Thread principal */
int kernel_func(void *data) 
{
	//allow_signal(SIGKILL);
	printk(KERN_ALERT "Iniciado el kernel thread: Control del Clipboard: %s.\n", __this_module.name);
	set_current_state(TASK_INTERRUPTIBLE);	
	schedule();
	while (!kthread_should_stop()){
	/*	
	if (signal_pending(current)) { 
			printk(KERN_ALERT "No puede matarse este proceso, desinstale el modulo para parar el thread\n");
		}
	*/
		printk(KERN_ALERT "Contenido del buffer del clipboard %s: %s\n", __this_module.name, clipboard_buffer);
		printk(KERN_ALERT "Numero de caracteres del buffer: %i\n", strlen(clipboard_buffer));
		set_current_state(TASK_INTERRUPTIBLE);	
		schedule();
	}
	printk(KERN_ALERT "Finalizado el kernel thread: Control_Clipboards.\n");
	return 0;
}

/* Thread creado al escribir en "PERIOD" */
int kernel_func_wake(void *data)
{
	allow_signal(SIGKILL);
	printk(KERN_ALERT "Iniciado el kernel thread: Despierta kernel cada 10s.\n");
	while (!kthread_should_stop()){
		if (signal_pending(current)) {
			printk(KERN_ALERT "Matado el thread de repeticion del thread principal\n");
			active_wake = 0;
			break;
		}
		wake_up_process(ts);
		// Hacemos la espera interrumpible para que si se recibe una señal
		// el procesamiento sea inmediato
		msleep_interruptible(10000);
	}
	printk(KERN_ALERT "Finalizado el kernel thread: Despierta kernel cada 10s.\n");
	return 0;
}
/**************************** FIN FUNCIONES THREADS************* *******************************/ 

/**************************** INICIO FUNCIONES LECTURA/ESCRITURA *******************************/ 
/* Funcion de lectura del clipboard activo */
int read_proc_clipboard(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data)
{
	if (offset > 0 || clipboard_buffer == NULL) {
		return 0;
	} else {
		memcpy(buffer, clipboard_buffer, strlen(clipboard_buffer));			
	}
	return strlen(clipboard_buffer);
}

/* Funcion de lectura del clipboard "periodo" */
int read_proc_period(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data)
{
	if (offset > 0 || period_buffer == NULL) {
		return 0;
	} else {
		memcpy(buffer, period_buffer, strlen(period_buffer));	
	}
	return strlen(period_buffer);
}

/* Funcion de escritura del clipboard activo */
int write_proc_clipboard(struct file *filepointer, const char __user *buffer, unsigned long len, void *data)
{
	int length;
	if (len > MAX_LENGTH) {
		length = MAX_LENGTH;
	} else {
		length = len;
	}
	// Escritura en el clip 
	if (copy_from_user(my_buffer, buffer, length)) {
		printk(KERN_ALERT "Imposible escribir\n");
		return -EFAULT;
	}

	sscanf(my_buffer, "%s", clipboard_buffer);
	if (active_wake == 0)
		wake_up_process(ts);

	return length;
}

/* Funcion de escritura del clipboard "periodo" */
int write_proc_period(struct file *filepointer, const char __user *buffer, unsigned long len, void *data)
{
	long i = simple_strtol(buffer, NULL, 10);
	int length;
	if (len > MAX_LENGTH) {
		length = MAX_LENGTH;
	} else {
		length = len;
	}
	// Escritura en el clip
	if (copy_from_user(my_buffer, buffer, length)) {
		printk(KERN_ALERT "Imposible escribir\n");
		return -EFAULT;
	}	

	sscanf(my_buffer, "%s", period_buffer);
	/* Si escribimos algo distinto de 0 y el thread no esta arrancado, lo arrancamos */
	if (i != 0 && active_wake == 0) {
		ts_wake = kthread_run(kernel_func_wake,NULL,"Despierta_Control");
		active_wake = 1;
	/* Si escribimos 0 y el thread esta arrancado, lo paramos */
	} else if (active_wake != 0 && i == 0) {
		kthread_stop(ts_wake);
		active_wake = 0;
	}

	return length;
}
/**************************** FIN FUNCIONES LECTURA/ESCRITURA *********************************/ 

/* Funcion invocada al cargar el modulo */
int modulo_carga(void) 
{
	clipboard_buffer = (char *)vmalloc(MAX_LENGTH);	
	period_buffer = (char *)vmalloc(MAX_LENGTH);
	my_buffer = (char *)vmalloc(MAX_LENGTH);
	// Inicialmente el thread secundario esta parado
	active_wake = 0;
	
	printk(KERN_ALERT "Cargado el modulo %s\n",__this_module.name);
	
	// Creamos el directorio con el nombre del clipboard
	if (create_proc_dir_custom(__this_module.name, &proc_dir, &proc_dir_aiso) == -ENOMEM) return -ENOMEM;
	
	// Creamos la entrada CLIPBOARD
	if (create_proc_entry_custom(CLIPBOARD, &proc_clipboard, &proc_dir) == -ENOMEM) return -ENOMEM;
	// Asociamos funciones de lectura y escritura y damos permisos
	proc_clipboard->read_proc		= read_proc_clipboard;
	proc_clipboard->write_proc 		= write_proc_clipboard;
	proc_clipboard->mode 			= S_IFREG | S_IRUGO;

	// Creamos la entrada PERIOD
	if (create_proc_entry_custom(PERIOD, &proc_period, &proc_dir) == -ENOMEM) return -ENOMEM;
	// Asociamos funciones de lectura y escritura y damos permisos
	proc_period->read_proc			= read_proc_period;
	proc_period->write_proc 		= write_proc_period;
	proc_period->mode 				= S_IFREG | S_IRUGO;

	// Creamos el kernel thread
  	ts = kthread_run(kernel_func,NULL,"Control_Clipboards"); 
	return 0;
} 

/* Funcion invocada al descargar el modulo */
void modulo_descarga(void) 
{
	printk(KERN_ALERT "Descargado el modulo %s\n", __this_module.name);
	
	// Eliminamos la entrada CLIPBOARD
	remove_proc_entry_custom(CLIPBOARD, &proc_clipboard, &proc_dir);
	
	// Eliminamos la entrada PERIOD
	remove_proc_entry_custom(PERIOD, &proc_period, &proc_dir);
	
	// Eliminamos el directorio con el nombre del clip
	remove_proc_dir_custom(__this_module.name, &proc_dir, &proc_dir_aiso);
	
	// Eliminamos el kernel thread
	kthread_stop(ts);
	// Eliminamos el segundo kernel thread si esta activo
	if (active_wake == 1) {	
		kthread_stop(ts_wake);
	}
	// Limpiamos los buffers
	vfree(period_buffer);
	vfree(clipboard_buffer);
	vfree(my_buffer);
}

/* Funciones init y exit */
module_init(modulo_carga);
module_exit(modulo_descarga);